import axios, { AxiosError, AxiosInstance } from "axios"
import NProgress from "nprogress"

import { checkStatus } from "@/api/helper/checkStatus"
import { timeOutFunc } from "@/api/helper/timeOut"
import { Toast } from "@/components/messages"
import { LOGIN_CODE, Result, ResultEnum } from "@/interface/http"
import { EAxiosResponse, RequestConfig } from "@/interface/http"
import { history } from "@/routes/history"
import store from "@/stores"
import { setIpInfo, setMaintainInfo } from "@/stores/global/global.store"
import { setToken } from "@/stores/user/user.store"
import { commonHeaders, decryptData, encryptData } from "@/utils/encrypt"
import { AxiosCanceler } from "@/utils/http/axiosCancel"
import Finger from "@/utils/http/fingerprint"

Finger.load()
// 是否需要加密

// const isEncrypted = import.meta.env.NODE_ENV === "production" && import.meta.env.VITE_APP_ENCRYPTED === "1";
const isEncrypted = import.meta.env.VITE_APP_ENCRYPTED === "1"

const axiosCanceler = new AxiosCanceler()

const config = {
	// 默认地址请求地址，可在 .env 开头文件中修改
	baseURL: import.meta.env.VITE_API_URL as string,
	// 设置超时时间（15s）
	timeout: 15000,
	// 跨域时候允许携带凭证
	withCredentials: true
}

const configB = {
	...config,
	baseURL: import.meta.env.VITE_B_API_URL as string
}

export class RequestHttp {
	service: AxiosInstance
	public constructor(config: RequestConfig) {
		// 实例化axios
		this.service = axios.create(config)
		/**
		 * @description 请求拦截器
		 * 客户端发送请求 -> [请求拦截器] -> 服务器
		 * token校验(JWT) : 接受服务器返回的token,存储到redux/本地储存当中
		 */
		this.service.interceptors.request.use(
			async (config: RequestConfig) => {
				NProgress.start()
				// * 将当前请求添加到 pending 中
				axiosCanceler.addPending(config)
				// * 如果当前请求不需要显示 loading,在api服务中通过指定的第三个参数: { headers: { noLoading: true } }来控制不显示loading，参见loginApi
				// config.headers!.noLoading || store.dispatch(setGlobalState({ loading: true }))
				config.language = store.getState().global.locale
				config.token = store.getState().user.token
				config = await commonHeaders(config)
				if (isEncrypted && !config.data?.append) {
					// 加密
					config = encryptData(config)
				}

				return config
			},
			(error: AxiosError) => {
				return Promise.resolve([error])
			}
		)

		/**
		 * @description 响应拦截器
		 *  服务器换返回信息 -> [拦截统一处理] -> 客户端JS获取到信息
		 */
		this.service.interceptors.response.use(
			async (response: EAxiosResponse) => {
				NProgress.done()
				// * 在请求结束后，移除本次请求(关闭loading)
				// tryHideFullScreenLoading()
				// store.dispatch(setGlobalState({ loading: false }))
				const headersContentType = response.headers["content-type"] || ""
				if (
					isEncrypted &&
					response.data &&
					(headersContentType.includes("application/json") || headersContentType.includes("text/plain"))
				) {
					// 解密
					try {
						const responseDecryptData = decryptData(response.data) || ""
						response.data = JSON.parse(responseDecryptData)
					} catch (error) {
						console.log(error)
					}
				}
				const { data, config } = response
				// convert blob to json
				if (data instanceof Blob) {
					// Check the content-type to decide how to handle the Blob
					if (headersContentType.includes("application/json")) {
						const blobToJson = (blob: Blob) => {
							return new Promise((resolve, reject) => {
								const reader = new FileReader()
								reader.onload = function (event) {
									try {
										const jsonData = JSON.parse(event.target?.result as any)
										if (jsonData.code === ResultEnum.SUCCESS) {
											Toast.error(jsonData.msg)
										}
										resolve(jsonData)
									} catch (error) {
										reject(error)
									}
								}
								// reader.onerror = reject
								reader.readAsText(blob)
							})
						}

						try {
							const jsonData: any = await blobToJson(data)
							Toast.error(jsonData.msg)
							return Promise.resolve([jsonData, response]) // Return the response with JSON data
						} catch (error) {
							return Promise.reject(error) // Reject the promise if an error occurs
						}
					}
				}
				axiosCanceler.removePending(config)
				// * 登录失效（code == 599）
				if (data.code == ResultEnum.OVERDUE) {
					// 请求异常
					store.dispatch(setToken({ token: "" }))

					Toast.error(data.msg)
					history.push("/login")
					return Promise.resolve([data])
				}
				if (data.code == ResultEnum.MANY_DATA) {
					Toast.error(data.msg)
				}
				// * 全局错误信息拦截（防止下载文件得时候返回数据流，没有code，直接报错）
				if (!data.code) {
					data.msg && Toast.error(data.msg)
					return Promise.resolve([data, response])
				}
				if (data.code && data.code !== ResultEnum.SUCCESS) {
					//请求异常
					if (LOGIN_CODE.includes(data.code)) {
						// 登录失效
						const token = store.getState().user.token
						if (token) {
							Toast.error(data.msg)
						}

						localStorage.removeItem("t")
						history.push("/login")
					} else if (data.code === ResultEnum.IP_LIMIT) {
						// IP限制
						const isLock = store.getState().global.ipInfo.isLock
						if (!isLock) {
							store.dispatch(
								setIpInfo({
									isLock: true,
									ip: data?.data?.ip,
									region: data?.data?.region
								})
							)
						}
					} else if (data.code === ResultEnum.MAINTAIN_CODE_ERROR) {
						// 维护
						const isLock = store.getState().global.maintainInfo.isLock
						if (!isLock) {
							store.dispatch(
								setMaintainInfo({
									isLock: true,
									info: data?.data
								})
							)
						}
					} else {
						Toast.error(data.msg)
					}

					return Promise.resolve([data])
				}

				if (config.successTip) {
					Toast.info(config.successTip)
				}

				// * 成功请求（在页面上除非特殊情况，否则不用处理失败逻辑）
				// const isLock = store.getState().global.ipInfo.isLock;
				// if (isLock) {
				// 	store.dispatch(
				// 		setIpInfo({
				// 			isLock: false
				// 		})
				// 	);
				// }
				return [null, data.data]
			},
			async (error: AxiosError) => {
				const { response } = error
				NProgress.done()
				// tryHideFullScreenLoading()
				// store.dispatch(setGlobalState({ loading: false }))
				// 请求超时单独判断，请求超时没有 response
				if (error.message.indexOf("timeout") !== -1) {
					// Toast.error(formatMessage({ id: "response.timeout" }))
					timeOutFunc()
				}
				// 根据响应的错误状态码，做不同的处理
				if (response) checkStatus(response.status)
				// 服务器结果都没有返回(可能服务器错误可能客户端断网) 断网处理:可以跳转到断网页面
				if (!window.navigator.onLine) {
					console.error("断网")
					// window.location.href = "/500"
				}
				return Promise.resolve([error])
			}
		)
	}

	// * 常用请求方法封装
	get<T>(url: string, params?: object, _object = {}): Promise<[Result, T]> {
		return this.service.get(url, { params, ..._object })
	}
	post<T>(url: string, params?: object, _object = {}): Promise<[Result, T]> {
		return this.service.post(url, params, _object)
	}
	put<T>(url: string, params?: object, _object = {}): Promise<[Result, T]> {
		return this.service.put(url, params, _object)
	}
	delete<T>(url: string, params?: any, _object = {}): Promise<[Result, T]> {
		return this.service.delete(url, { params, ..._object })
	}
}

export const httpB = new RequestHttp(configB)

export default new RequestHttp(config)
